﻿#define _CAMERABOX_TRACKBAR_ENABLE

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

using Declarations;
using Declarations.Events;
using Declarations.Media;
using Declarations.Players;
using Implementation;

namespace SDK10kWrapper
{
    partial class CameraBox
    {
        #region Multiple TrackBar Per Camera
        #region Property
        TrackBar _Offline_SlideBar = null;
        /// <summary>
        /// <para>This Property support Single Binding to one TrackBar only.</para>
        /// <para>If you want multiple TrackBar Binding, mark public method TrackBarBinding & Unbinding instead</para>
        /// </summary>
        [DefaultValue(null), Category(".Camera"), Description("Binding Slide Bar for this Playback Video")]
        public TrackBar Offline_SlideBar
        {
            get { return _Offline_SlideBar; }
            set
            {
                this._Offline_SlideBar = value;
                #if _CAMERABOX_TRACKBAR_ENABLE
                if (this._Offline_SlideBar == null) return; // null then nothing change
                if (Util.DesignMode) return;
                this.TrackBarUnbinding(_Offline_SlideBar); // Unbind old SlideBar (if null then will return)
                this.TrackBarBinding(_Offline_SlideBar); // Add Binding to this Slide Bar
#endif
            }
        }

        float _Offline_VideoPlayPosition = 0; // Don't forget to implement Init Value to m_player.Position in _Load() Event
        [DefaultValue(0), Category(".Camera"), Description("Set (Binding Support) Playback Position (0.0 - 1.0 equal 0% - 100%) of the Video")]
        public float Offline_VideoPlayPosition
        {
            get { return _Offline_VideoPlayPosition; }
            set
            {
                if (value != _Offline_VideoPlayPosition)
                {
                    // To use this simple comparation. Value is 100 range while float is more than that.
                    // So will cause value = 0.005 but _Offline_VideoPlayPosition = 0.005526. Need to add threshold check in Parse & Format
                    _Offline_VideoPlayPosition = value;
                    if (_CameraMode == SDK10kWrapper.CameraMode.Offline & m_player != null)
                        m_player.Position = _Offline_VideoPlayPosition; // > 1 ? 1 : _Offline_VideoPlayPosition; // Really need this or binding already do its job ?
                }
            }
        }
        #endregion
        #region Method
#if true
        /// <summary>
        /// Support Multiple Trackbar Binding at the sametime. Information for each TrackBar will be save to bDict
        /// </summary>
        /// <param name="trackBar">The TrackBar or SlideBar you want to bind with</param>
        public void TrackBarBinding(TrackBar trackBar)
        {   //http://msdn.microsoft.com/en-us/library/system.windows.forms.binding.format%28v=vs.85%29.aspx
            if (trackBar == null) return;

            if (tbDict.ContainsKey(trackBar)) return; // Already Bind then return

            // Binding Two-Way
            Binding b = new Binding("Value", this, "Offline_VideoPlayPosition", false, DataSourceUpdateMode.OnPropertyChanged);
            b.Format += (s, e) => // float => int
            {
                if (e.DesiredType != typeof(int)) return; // The method converts only to string type. Test this using the DesiredType.

                // When first load Binding, why sometime e.Value = 50F out of range (0-1) why ?

                TrackBar trackbar = (s as Binding).Control as TrackBar;

                int val = Convert.ToInt32((float)e.Value * trackbar.Maximum); // value = val * max = pos * max

                // if (tcDict.ContainsKey(trackbar)) if (tcDict[trackbar].Count > 1) val = (int)(val * this.duration / MaxDuration);

                e.Value = val > trackbar.Maximum ? trackbar.Maximum : val; // Avoid over scale
            };
            b.Parse += (s, e) => // int => float
            {
                if (e.DesiredType != typeof(float)) return; // The method converts only to string type. Test this using the DesiredType.

                TrackBar trackbar = (s as Binding).Control as TrackBar;

                float pos = Convert.ToSingle(e.Value) / trackbar.Maximum; // pos = val = value / max

                // if (tcDict.ContainsKey(trackbar)) if (tcDict[trackbar].Count > 1) pos = pos * MaxDuration / this.duration;

                e.Value = pos > 1 ? 1 : pos; // Avoid overscale
            };

            trackBar.DataBindings.Add(b);   // Add binding to Trackbar (2-way)

            tbDict.Add(trackBar, b); // Remember this Binding with this Trackbar by bDict (after Bind Success)

#if IMPLEMENTED_BINDING_ANYTRACKBAR
            lock (locker)
            {
                if (!tcDict.ContainsKey(trackBar)) tcDict.Add(trackBar, new List<CameraBox>());
                tcDict[trackBar].Add(this);
            }
#endif
        }


        /// <summary>
        /// Unbinding multiple Trackbar with this video playback
        /// </summary>
        /// <param name="trackBar">The Trackbar that you want to unbind</param>
        public void TrackBarUnbinding(TrackBar trackBar)
        {
            if (trackBar == null) return; // Null return

            if (!tbDict.ContainsKey(trackBar)) return; // No binding found => return

            Binding b = tbDict[trackBar];

            if (b.BindableComponent != null) trackBar.DataBindings.Remove(b); // Remove binding from Trackbar

            tbDict.Remove(trackBar); // Update bDict (remove this binding from memory)

#if IMPLEMENTED_BINDING_ANYTRACKBAR
            lock (locker)
            {
                if (tcDict.ContainsKey(trackBar))
                {
                    tcDict[trackBar].Remove(this);
                    if (tcDict[trackBar].Count == 0) tcDict.Remove(trackBar);
                }
            }
#endif
        }
#endif
        #endregion
        #endregion

        #region Multiple Camera Per Trackbar

#if false
        TrackBar _Offline_SlideBarForAll = null;
        /// <summary>
        /// <para>Add Trackbar for All of your.</para>
        /// <para>If you want multiple TrackBar Binding, mark public method TrackBarBinding & Unbinding instead</para>
        /// </summary>
        [DefaultValue(null), Category(".Camera"), Description("Binding Slide Bar for this Playback Video")]
        public TrackBar Offline_SlideBarForAll
        {
            get { return _Offline_SlideBarForAll; }
            set
            {
                //this._Offline_SlideBarForAll = value;
                foreach (CameraBox cambox in cList) // Always remember foreach already include this._
                {
                    if (cambox.ParentForm == this.ParentForm)
                    {
                        cambox._Offline_SlideBarForAll = value;
                        if (cambox._Offline_SlideBarForAll == null) continue; // NEED CHECK continue HERE
                        if (Util.DesignMode) continue;
                        cambox.CameraUnbinding();
                        cambox.CameraBinding();
                    }
                }
            }
        }
#endif
        TrackBar _Offline_SlideBarForAll = null;
        /// <summary>
        /// <para>Trackbar (SlideBar) for all CameraBox in Form</para>
        /// <para>No binding use, just event instead</para>
        /// </summary>
        [DefaultValue(null), Category(".Camera"), Description("Add trackbar for all CameraBox in Form"), EditorBrowsable(EditorBrowsableState.Never)]
        public TrackBar Offline_SlideBarForAll
        {
            get { return _Offline_SlideBarForAll; }
            set
            {
                //this._Offline_SlideBarForAll = value;
                foreach (CameraBox cambox in cList) // Always remember foreach already include this._
                {
                    if (cambox.ParentForm == this.ParentForm)
                    {
                        cambox._Offline_SlideBarForAll = value;
                        if (cambox._Offline_SlideBarForAll == null) continue; // NEED CHECK continue HERE
                        if (Util/*cambox*/.DesignMode) continue;
                        cambox.RemoveEvent();
                        cambox.AddEvent();
                    }
                }
            }
        }


        #region PRIVATE MEMBERS
        /// <summary>
        /// Trackbar Binding Dict (for Multiple Trackbar to one CameraBox)
        /// </summary>
        Dictionary<TrackBar, Binding> tbDict = new Dictionary<TrackBar, Binding>();

        /// <summary>
        /// Duration of the Media Playback Video
        /// </summary>
        long duration;


        float _oldpos;

        EventHandler _eventhandler;
        public void AddEvent()
        {
            _eventhandler = new EventHandler(_Offline_SlideBarForAll_Scroll);
            _Offline_SlideBarForAll.Scroll += _eventhandler;
        }

        void _Offline_SlideBarForAll_Scroll(object sender, EventArgs e)
        {
            TrackBar trackbar = sender as TrackBar;
            m_player.Time = trackbar.Value * MaxDuration / trackbar.Maximum;
        }

        public void RemoveEvent()
        {
            if (_eventhandler != null && _Offline_SlideBarForAll != null) _Offline_SlideBarForAll.Scroll -= _eventhandler;
        }

        /// <summary>
        /// Binding multiple CameraBox to single Offline_SlideBarForAll
        /// </summary>
        public void CameraBinding()
        {   //http://msdn.microsoft.com/en-us/library/system.windows.forms.binding.format%28v=vs.85%29.aspx
            if (_Offline_SlideBarForAll == null) return;

            // Binding Two-Way
            Binding b = new Binding("Offline_VideoPlayPosition", _Offline_SlideBarForAll, "Value", false, DataSourceUpdateMode.OnPropertyChanged);
            b.Format += (s, e) => // int => float
            {
                if (e.DesiredType != typeof(float)) return; // The method converts only to string type. Test this using the DesiredType.

                TrackBar trackbar = (s as Binding).DataSource as TrackBar;

                float pos = Convert.ToSingle(e.Value) / trackbar.Maximum; // pos = val = value / max

                //pos = pos * MaxDuration / this.duration;

                if (ValueReturnByBinding2Ways(_oldpos, pos, (float)trackbar.SmallChange / trackbar.Maximum))
                    e.Value = _oldpos; // In threshold mean return from 2ways binding then got the old (correct) pos value
                else
                    e.Value = pos; // Out threshold mean new slide pos then : pos = val = value / max
            };
            b.Parse += (s, e) => // float => int
            {
                if (e.DesiredType != typeof(int)) return; // The method converts only to string type. Test this using the DesiredType.

                TrackBar trackbar = (s as Binding).DataSource as TrackBar;

                _oldpos = (float)e.Value; // Remember current pos to use in Format (to check for return value from different scale 2ways binding by using threshold)

                int val = Convert.ToInt32((float)e.Value * trackbar.Maximum); // value = val * max = pos * max

                // val = (int)(val * this.duration / MaxDuration);

                e.Value = val > trackbar.Maximum ? trackbar.Maximum : val; // Avoid over scale
            };

            this.DataBindings.Add(b);   // Add Trackbar binding to this camera box (2-way)
        }

        bool ValueReturnByBinding2Ways(float videopos1, float videopos2, float threshold)
        {
            if (Math.Abs(videopos1 - videopos2) < threshold) return true; else return false;
            // If outside threshold mean value set by user drag the slidebar
            // otherwise trackbar.value resolution < _Offline_VideoPlayPosition then value always round 
        }

        /// <summary>
        /// Unbinding multiple CameraBox from _Offline_SlideBarForAll
        /// </summary>
        public void CameraUnbinding()
        {
            Binding b = this.DataBindings["Offline_VideoPlayPosition"];
            if (b != null) this.DataBindings.Remove(b); // Remove binding from Trackbar (Check if DataSource null then Unbind). 1 case that b != null but DataSource = null then Remove still fail ?
        }

#if false
        Dictionary<TrackBar, float> pDict = new Dictionary<TrackBar, float>();
        /// <summary>
        /// Support Multiple Camera Binding to one Trackbar.
        /// </summary>
        /// <param name="trackBar">The TrackBar or SlideBar you want to bind to</param>
        public void CameraBinding(TrackBar trackBar)
        {   //http://msdn.microsoft.com/en-us/library/system.windows.forms.binding.format%28v=vs.85%29.aspx
            if (trackBar == null) return;

            //if (bDict.ContainsKey(trackBar)) return; // Already Bind then return

            // Binding Two-Way
            Binding b = new Binding("Offline_VideoPlayPosition", trackBar, "Value");//, false, DataSourceUpdateMode.OnPropertyChanged);
            b.Format += (s, e) => // e.Value : trackbar.Value
            {
                if (e.DesiredType != typeof(float)) return; // The method converts only to string type. Test this using the DesiredType.
                TrackBar trackbar = (s as Binding).DataSource as TrackBar;
                float pos = Convert.ToSingle(e.Value) / trackbar.Maximum;
                if (ValueReturnByBinding2Ways(pDict[trackbar], pos, (float)trackbar.SmallChange / trackbar.Maximum))
                    e.Value = pDict[trackbar]; // In threshold mean return from 2ways binding then got the old (correct) pos value
                else
                    e.Value = pos; // Out threshold mean new slide pos then : pos = val = value / max
            };
            b.Parse += (s, e) => // e.Value : _Offline_VideoPlayPosition
            {   // Happen after PropertyChanged("...VideoPlayPosition")
                if (e.DesiredType != typeof(int)) return; // The method converts only to string type. Test this using the DesiredType.
                float pos = (float)e.Value; // 5if (pos > 1) return; // When first load Binding, Format Event will be called & e.Value = 50 out of range (0-1) why ?
                TrackBar trackbar = (s as Binding).DataSource as TrackBar; // Get Trackbar
                pDict[trackbar] = pos;  // Remember current pos to use in Format (to check for return value from different scale 2ways binding by using threshold)
                e.Value = (int)(pos * trackbar.Maximum); // value = val * max = pos * max
            };

            //bDict.Add(trackBar, b); // Remember this Binding with this Trackbar by bDict
            //pDict.Add(trackBar, 0);

            this.DataBindings.Add(b);   // Add binding to Trackbar (2-way)
        }

        bool ValueReturnByBinding2Ways(float videopos1, float videopos2, float threshold)
        {
            if (Math.Abs(videopos1 - videopos2) > threshold) return false; else return true;
            // If outside threshold mean value set by user drag the slidebar
            // otherwise trackbar.value resolution < _Offline_VideoPlayPosition then value always round 
        }

        /// <summary>
        /// Unbinding multiple Camera with this Trackbar
        /// </summary>
        /// <param name="trackBar">The Trackbar that you want to Unbind from</param>
        public void CameraUnbinding(TrackBar trackBar)
        {
            if (trackBar == null) return; // Null return

            if (!tbDict.ContainsKey(trackBar)) return; // No binding found => return

            this.DataBindings.Remove(tbDict[trackBar]); // Remove binding from Trackbar

            tbDict.Remove(trackBar); // Update bDict (remove this binding from memory)
        }
#endif
        #endregion
        #endregion
    }
}